package com.heqiang.spring.beans.factory.xml;

import cn.hutool.core.bean.BeanException;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.heqiang.spring.beans.BeansException;
import com.heqiang.spring.beans.PropertyValue;
import com.heqiang.spring.beans.factory.config.BeanDefinition;
import com.heqiang.spring.beans.factory.support.AbstractBeanDefinitionReader;
import com.heqiang.spring.beans.factory.support.BeanDefinitionRegistry;
import com.heqiang.spring.beans.factory.config.BeanReference;
import com.heqiang.spring.context.annotation.ClassPathBeanDefinitionScanner;
import com.heqiang.spring.core.io.Resource;
import com.heqiang.spring.core.io.loader.ResourceLoader;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.List;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader) {
        super(registry,resourceLoader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            try (InputStream inputStream = resource.getInputStream()) {
                doLoadBeanDefinitions(inputStream);
            }
        } catch (Exception e) {
            throw new BeansException("IOException parsing XML document from " + resource, e);
        }
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader loader = getResourceLoader();
        Resource resource = loader.getResource(location);
        loadBeanDefinitions(resource);

    }

    @Override
    public void loadBeanDefinitions(String... locations) throws BeansException {
        for (String location : locations) {
            loadBeanDefinitions(location);
        }
    }


    protected void doLoadBeanDefinitions(InputStream inputStream) throws Exception {
        SAXReader saxReader = new SAXReader();
        Document doc = saxReader.read(inputStream);
        Element root = doc.getRootElement();

        Element componentScan = root.element("component-scan");
        if (null != componentScan) {
            String canPath = componentScan.attributeValue("base-package");
            if (StrUtil.isEmpty(canPath)) {
                throw new BeanException("The value of base-package attribute can not be empty or null");
            }
            scanPackage(canPath);
        }

        List<Element> beanList = root.elements("bean");
        for (Element bean : beanList) {
            String id = bean.attributeValue("id");
            String name = bean.attributeValue("name");
            String className = bean.attributeValue("class");
            String initMethodName = bean.attributeValue("init-method");
            String destroyMethodName = bean.attributeValue("destroy-method");
            String beanScope = bean.attributeValue("scope");

            // 获取 Class，方便获取类中的名称
            Class<?> clazz = Class.forName(className);
            //优先级：id>name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }
            //定义Bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethodName);
            beanDefinition.setDestroyMethodName(destroyMethodName);
            if (StrUtil.isNotEmpty(beanScope)) {
                beanDefinition.setScope(beanScope);
            }
            //读取属性并填充
            List<Element> propertyList = bean.elements("property");
            for (Element property : propertyList) {
                String attName = property.attributeValue("name");
                String attValue = property.attributeValue("value");
                String attRef = property.attributeValue("ref");

                Object value = StrUtil.isNotEmpty(attRef)?new BeanReference(attRef) : attValue;

                PropertyValue propertyValue = new PropertyValue(attName,value);
                beanDefinition.getPropertyValues().addProperValue(propertyValue);
            }
            if (getRegistry().containsBeanDefition(beanName)) {
                    throw new BeansException("重复注册相同的bean");
            }
            getRegistry().registerBeanDefinition(beanName,beanDefinition);
        }
    }


    private void scanPackage(String canPath) {
        String[] basePackages = StrUtil.splitToArray(canPath, ',');
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
        scanner.doScan(basePackages);
    }
}
